package cn.yjh;

import cn.yjh.api.PluginService;
import cn.yjh.config.ConfigurationManager;
import cn.yjh.constant.CommandEnum;
import cn.yjh.context.MonitorContext;

import cn.yjh.server.net.MasterReactor;
import cn.yjh.utils.MonitoryFactoriesLoader;
import cn.yjh.utils.StringUtils;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @DESC:
 * @AUTHOR: YouJinhua
 * @DATE: 2021-11-25 23:31:54
 * @VERSION: 1.0
 */
public abstract class AbstractMonitorApplication implements MonitorApplication{

    public String agent_root_path;

    public final ConfigurationManager configurationManager;

    public AbstractMonitorApplication(){
        configurationManager = new ConfigurationManager();
    }

    @Override
    public void startApplication(String commandArgs) throws Exception{

      /*  // 打印启动 LOGO
        printBanner();

        // 获取当前运行类所在的目录
        //initAgentRootPath();

        // 加载配置文件
        loadConfiguration(commandArgs);

        // 加载依赖 jar 包
        loadOptionalDependency();*/

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        //  注册监听器
        registListener(classLoader);

        // 加载插件
        List<String> strings = MonitoryFactoriesLoader.loadFactoryNames(PluginService.class, null);

        // 开启一个守护线程，启动 RPC 服务端，让用户能够通过发送请求，修改配置
        Thread thread = new Thread((new MasterReactor(2088,"cn.yjh.api")));
        thread.setDaemon(true);
        thread.setName("MASTER");
        thread.start();
    }

    @Override
    public void shutDownApplication() {

    }

    public void registListener(ClassLoader classLoader) throws Exception {
        Class<?> clazz = classLoader.loadClass("cn.yjh.listener.ConfigurationListener");
        Constructor<?> constructor = clazz.getConstructor();
        constructor.setAccessible(true);
        Listener listener = (Listener) constructor.newInstance();
        MonitorContext.multicaster.addListener(listener);
    }

    // 加载可选依赖，比如：slf4j 等
    public void loadOptionalDependency() throws Exception {
        boolean isDefaultLogger = true;
        try{
            Class.forName("org.slf4j.LoggerFactory");
        }catch (ClassNotFoundException e){
            isDefaultLogger = false;
        }
        File file = new File(agent_root_path,"/lib/opt/");
        if(!file.exists()){
            throw new RuntimeException("the optional dependency dir [ "+agent_root_path+"/lib/opt ] not exist !");
        }

        List<URL> urlList = new ArrayList<>();
        for (String fileName: file.list()){
            if(isDefaultLogger && fileName.indexOf("slf4j-api") != -1){
                continue;
            }
            File jarFile = new File(file.getPath() , fileName);
            URL url = jarFile.toURI().toURL();
            urlList.add(url);
        }

        // 给应用类加载器添加依赖
        /*ClassLoader appLoader = Thread.currentThread().getContextClassLoader();
        Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
        addURL.setAccessible(true);
        for(URL url:urlList){
            addURL.invoke(appLoader,url);
        }*/

        // 创建新的类加载器
        URL[] urLs = new URL[urlList.size()];
        urlList.toArray(urLs);
        URLClassLoader urlClassLoader = new URLClassLoader(urLs);
        Thread.currentThread().setContextClassLoader(urlClassLoader);

        if(isDefaultLogger){
            outPrintln("use application Logger");
        }else {
            outPrintln("use default Logger: [ logback ]");
        }
    }



    private void loadConfiguration(String commandArgs) throws Exception {
        Properties prop = new Properties();
        // 加载命令行参数
        Optional.ofNullable(commandArgs).ifPresent(args->{
            byte[] bytes = args.getBytes(StandardCharsets.UTF_8);
            try (InputStream in = new ByteArrayInputStream(bytes);){
                prop.load(in);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        Object configLocation = prop.get(CommandEnum.CONFIG_LOCATION.KEY);
        // 根据命令行参数，优先加载命令行参数中指定的配置文件
        if(StringUtils.isNotEmpty(configLocation)){
            File file = new File(configLocation.toString());
            if(!file.exists()){
                throw new RuntimeException("the conf file [ "+configLocation+" ] not exist !");
            }
            //logger.info("load config from path：{}",configLocation);
            try (InputStream in = new FileInputStream(file);){
                prop.load(in);
                configurationManager.addConfigResource(ConfigurationManager.ACTIVE_CONFIGURATION,prop);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(configurationManager.hasActiceConfiguration()){

        }

        agent_root_path = Optional.ofNullable(agent_root_path).orElse(prop.get("agent_path").toString());
        File file = new File(agent_root_path,"/conf/bootstrap.properties");
        if(!file.exists()){
            throw new RuntimeException("the conf file [ "+agent_root_path+"/conf/bootstrap.properties ] not exist !");
        }
        //logger.info("load config from path：{}",agentRootPath);
        InputStream in = new FileInputStream(file);
        if(configurationManager.hasActiceConfiguration()){
            Properties properties = new Properties();
            properties.load(in);
            configurationManager.addConfigResource("bootstrapConfiguration",properties);
        }else{
            prop.load(in);
            configurationManager.addConfigResource(ConfigurationManager.ACTIVE_CONFIGURATION,prop);
        }
    }

    // 获取当前启动类所属 jar 包所在的路径
    public void initAgentRootPath() throws Exception {
        URL url = MonitorBootStrap.class.getProtectionDomain().getCodeSource().getLocation();
        String rootPath = URLDecoder.decode(url.getPath(), "UTF-8");
        if(url.getProtocol().equals("file")){
            File parentFile = new File(rootPath).getParentFile();
            rootPath = parentFile.getPath();
            this.agent_root_path = rootPath;
        }
        // 获取当前操作系统名称
        String systemType = System.getProperty("os.name").toUpperCase(Locale.ROOT);
        String path = url.getPath();
        if(systemType.indexOf("WINDOWS")!=-1){
            path = path.substring(1,path.lastIndexOf("/"));
        }else{
            path = path.substring(0,path.lastIndexOf("/"));
        }
        this.agent_root_path = path;
    }

    public void printBanner() {
        InputStream in = ClassLoader.getSystemResourceAsStream("LOGO.txt");
        try (InputStreamReader reader = new InputStreamReader(in, "utf-8");
             BufferedReader br = new BufferedReader(reader)) {
            while ((br.read()) != -1) {
                System.out.println("\033[1;36m" + br.readLine());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("\033[0m");
    }

    public static void outPrintln(String str ){
        DateFormat df=new SimpleDateFormat("[ yyyy-MM-dd:hh:mm:ss ]");
        System.out.println(df.format(new Date())+" "+str);
    }



}
